/*      */ package com.zimbra.cs.db;
/*      */ 
/*      */ import com.google.common.base.Objects;
/*      */ import com.google.common.base.Objects.ToStringHelper;
/*      */ import com.google.common.base.Strings;
/*      */ import com.google.common.collect.Multimap;
/*      */ import com.zimbra.common.localconfig.DebugConfig;
/*      */ import com.zimbra.common.localconfig.KnownKey;
/*      */ import com.zimbra.common.localconfig.LC;
/*      */ import com.zimbra.common.service.ServiceException;
/*      */ import com.zimbra.common.util.ListUtil;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.StringUtil;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.cs.imap.ImapMessage;
/*      */ import com.zimbra.cs.index.DbSearchConstraints;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.CursorRange;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.Intersection;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.Leaf;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.NumericRange;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.Range;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.RangeType;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.StringRange;
/*      */ import com.zimbra.cs.index.DbSearchConstraints.Union;
/*      */ import com.zimbra.cs.index.SortBy;
/*      */ import com.zimbra.cs.index.SortBy.Direction;
/*      */ import com.zimbra.cs.index.SortBy.Key;
/*      */ import com.zimbra.cs.mailbox.Flag;
/*      */ import com.zimbra.cs.mailbox.Flag.FlagInfo;
/*      */ import com.zimbra.cs.mailbox.Folder;
/*      */ import com.zimbra.cs.mailbox.MailItem;
/*      */ import com.zimbra.cs.mailbox.MailItem.Type;
/*      */ import com.zimbra.cs.mailbox.MailItem.UnderlyingData;
/*      */ import com.zimbra.cs.mailbox.Mailbox;
/*      */ import com.zimbra.cs.mailbox.Tag;
/*      */ import java.sql.PreparedStatement;
/*      */ import java.sql.ResultSet;
/*      */ import java.sql.SQLException;
/*      */ import java.sql.Timestamp;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Collection;
/*      */ import java.util.Collections;
/*      */ import java.util.Comparator;
/*      */ import java.util.Iterator;
/*      */ import java.util.List;
/*      */ import java.util.Map.Entry;
/*      */ import java.util.Set;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public final class DbSearch
/*      */ {
/*      */   private static final int COLUMN_ID = 1;
/*      */   private static final int COLUMN_INDEXID = 2;
/*      */   private static final int COLUMN_TYPE = 3;
/*      */   private static final String SORT_COLUMN_ALIAS = "sortcol";
/*      */   private static final String MI_I_MBOX_FOLDER_DATE = "i_folder_id_date";
/*      */   private static final String MI_I_MBOX_PARENT = "i_parent_id";
/*      */   private static final String MI_I_MBOX_INDEX = "i_index_id";
/*      */   private static final String NO_HINT = "";
/*      */   
/*      */   public static enum FetchMode
/*      */   {
/*   76 */     ID,  MAIL_ITEM,  IMAP_MSG,  MODSEQ,  PARENT,  MODCONTENT;
/*      */     private FetchMode() {} }
/*   78 */   private static final byte[] APPOINTMENT_TABLE_TYPES = { MailItem.Type.APPOINTMENT.toByte(), MailItem.Type.TASK.toByte() };
/*      */   
/*      */   private final Mailbox mailbox;
/*      */   
/*      */   private final boolean dumpster;
/*      */   
/*   84 */   private final StringBuilder sql = new StringBuilder();
/*   85 */   private final List<Object> params = new ArrayList();
/*      */   
/*      */   public DbSearch(Mailbox mbox) {
/*   88 */     this.mailbox = mbox;
/*   89 */     this.dumpster = false;
/*      */   }
/*      */   
/*      */   public DbSearch(Mailbox mbox, boolean dumpster) {
/*   93 */     this.mailbox = mbox;
/*   94 */     this.dumpster = dumpster;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean isCaseSensitiveField(String fieldName)
/*      */   {
/*  105 */     int periodOffset = fieldName.lastIndexOf('.');
/*  106 */     String colNameAfterPeriod; String colNameAfterPeriod; if ((periodOffset <= 0) && (periodOffset < fieldName.length() + 1)) {
/*  107 */       colNameAfterPeriod = fieldName;
/*      */     } else {
/*  109 */       colNameAfterPeriod = fieldName.substring(periodOffset + 1);
/*      */     }
/*  111 */     return (colNameAfterPeriod.equals("sender")) || (colNameAfterPeriod.equals("subject")) || (colNameAfterPeriod.equals("name"));
/*      */   }
/*      */   
/*      */   private static String toSortField(SortBy sort)
/*      */   {
/*  116 */     Db db = Db.getInstance();
/*  117 */     switch (sort.getKey()) {
/*      */     case NONE: 
/*  119 */       return null;
/*      */     case SENDER: 
/*  121 */       return toStringSortField("mi.sender");
/*      */     case RCPT: 
/*  123 */       return toStringSortField("mi.recipients");
/*      */     case SUBJECT: 
/*  125 */       return toStringSortField("mi.subject");
/*      */     case NAME: 
/*      */     case NAME_NATURAL_ORDER: 
/*  128 */       return toStringSortField("mi.name");
/*      */     case ID: 
/*  130 */       return "mi.id";
/*      */     case SIZE: 
/*  132 */       return "mi.size";
/*      */     case ATTACHMENT: 
/*  134 */       return db.concat(new String[] { db.sign(db.bitAND("mi.flags", String.valueOf(Flag.BITMASK_ATTACHED))), db.lpad("mi.id", 10, "0") });
/*      */     
/*      */     case FLAG: 
/*  137 */       return db.concat(new String[] { db.sign(db.bitAND("mi.flags", String.valueOf(Flag.BITMASK_FLAGGED))), db.lpad("mi.id", 10, "0") });
/*      */     
/*      */     case PRIORITY: 
/*  140 */       return db.concat(new String[] { "(1 + " + db.sign(db.bitAND("mi.flags", String.valueOf(Flag.BITMASK_HIGH_PRIORITY))) + " - " + db.sign(db.bitAND("mi.flags", String.valueOf(Flag.BITMASK_LOW_PRIORITY))) + ")", db.lpad("mi.id", 10, "0") });
/*      */     }
/*      */     
/*      */     
/*      */ 
/*  145 */     return "mi.date";
/*      */   }
/*      */   
/*      */   private static String toStringSortField(String col)
/*      */   {
/*  150 */     Db db = Db.getInstance();
/*  151 */     if (Db.supports(Db.Capability.CASE_SENSITIVE_COMPARISON)) {
/*  152 */       return db.concat(new String[] { "UPPER(" + col + ")", db.lpad("mi.id", 10, "0") });
/*      */     }
/*  154 */     return db.concat(new String[] { col, db.lpad("CAST(mi.id AS CHAR)", 10, "0") });
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private void addSortColumn(SortBy sort)
/*      */   {
/*  162 */     String field = toSortField(sort);
/*  163 */     if (field == null) {
/*  164 */       return;
/*      */     }
/*  166 */     this.sql.append(", ").append(field).append(" AS ").append("sortcol");
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   static String orderBy(SortBy sort, boolean alias)
/*      */   {
/*  173 */     if (sort.getKey() == SortBy.Key.NONE) {
/*  174 */       return "";
/*      */     }
/*  176 */     return " ORDER BY " + (alias ? "sortcol" : toSortField(sort)) + (sort.getDirection() == SortBy.Direction.DESC ? " DESC" : "");
/*      */   }
/*      */   
/*      */   public int countResults(DbPool.DbConnection conn, DbSearchConstraints node) throws ServiceException
/*      */   {
/*  181 */     return countResults(conn, node, false);
/*      */   }
/*      */   
/*      */   public int countResults(DbPool.DbConnection conn, DbSearchConstraints node, boolean ignoreNoRecipients) throws ServiceException {
/*  185 */     node = node.optimize();
/*  186 */     this.sql.append("SELECT COUNT(*) FROM ").append(DbMailItem.getMailItemTableName(this.mailbox, "mi", this.dumpster));
/*  187 */     this.sql.append(" WHERE ");
/*  188 */     if (!DebugConfig.disableMailboxGroups) {
/*  189 */       this.sql.append("mi.mailbox_id = ? AND ");
/*  190 */       this.params.add(Integer.valueOf(this.mailbox.getId()));
/*      */     }
/*  192 */     encodeConstraint(node, null, false, false);
/*  193 */     if (ignoreNoRecipients) {
/*  194 */       this.sql.append(" AND mi.recipients IS NOT NULL");
/*      */     }
/*  196 */     PreparedStatement stmt = null;
/*  197 */     ResultSet rs = null;
/*      */     try {
/*  199 */       stmt = conn.prepareStatement(this.sql.toString());
/*  200 */       setParameters(stmt);
/*      */       
/*  202 */       rs = stmt.executeQuery();
/*  203 */       if (rs.next()) {
/*  204 */         return rs.getInt(1);
/*      */       }
/*  206 */       throw ServiceException.FAILURE("Failed to count DB search results", null);
/*      */     }
/*      */     catch (SQLException e) {
/*  209 */       throw ServiceException.FAILURE("Failed to count DB search results", e);
/*      */     } finally {
/*  211 */       conn.closeQuietly(rs);
/*  212 */       conn.closeQuietly(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */   private String getForceIndexClause(DbSearchConstraints node, SortBy sort, boolean hasLimit) {
/*  217 */     if (LC.search_disable_database_hints.booleanValue()) {
/*  218 */       return "";
/*      */     }
/*  220 */     if ((!Db.supports(Db.Capability.FORCE_INDEX_EVEN_IF_NO_SORT)) && (sort.getKey() == SortBy.Key.NONE)) {
/*  221 */       return "";
/*      */     }
/*  223 */     String index = null;
/*  224 */     if ((node instanceof DbSearchConstraints.Leaf)) {
/*  225 */       DbSearchConstraints.Leaf constraints = node.toLeaf();
/*  226 */       if (!constraints.itemIds.isEmpty())
/*  227 */         return "";
/*  228 */       if (constraints.convId > 0) {
/*  229 */         index = "i_parent_id";
/*  230 */       } else if (!constraints.indexIds.isEmpty()) {
/*  231 */         index = "i_index_id";
/*  232 */       } else if ((sort.getKey() == SortBy.Key.DATE) && (hasLimit))
/*      */       {
/*  234 */         if (constraints.getOnlyFolder() != null)
/*      */         {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  240 */           index = "i_folder_id_date";
/*      */         }
/*      */       }
/*      */     }
/*  244 */     return Db.forceIndex(index);
/*      */   }
/*      */   
/*      */   private void encodeSelect(SortBy sort, FetchMode fetch, boolean joinAppt, boolean joinTaggedItem, DbSearchConstraints node, boolean validLIMIT)
/*      */   {
/*  249 */     encodeSelect(sort, fetch, joinAppt, joinTaggedItem, node, validLIMIT, false);
/*      */   }
/*      */   
/*      */ 
/*      */   private void encodeSelect(SortBy sort, FetchMode fetch, boolean joinAppt, boolean joinTaggedItem, DbSearchConstraints node, boolean validLIMIT, boolean maybeExcludeHasRecipients)
/*      */   {
/*  255 */     this.sql.append("SELECT ");
/*  256 */     switch (fetch) {
/*      */     case ID: 
/*  258 */       this.sql.append("mi.id, mi.index_id, mi.type");
/*  259 */       break;
/*      */     case MAIL_ITEM: 
/*  261 */       this.sql.append("mi.id, mi.type, mi.parent_id, mi.folder_id, mi.prev_folders, mi.index_id,mi.imap_id, mi.date, mi.size, mi.locator, mi.blob_digest, mi.unread, mi.flags, mi.tag_names, mi.subject,mi.name, mi.metadata, mi.mod_metadata, mi.change_date, mi.mod_content, mi.uuid");
/*  262 */       break;
/*      */     case IMAP_MSG: 
/*  264 */       this.sql.append("mi.id, mi.index_id, mi.type, mi.imap_id, mi.unread, mi.flags, mi.tag_names");
/*  265 */       break;
/*      */     case MODSEQ: 
/*  267 */       this.sql.append("mi.id, mi.index_id, mi.type, mi.mod_metadata");
/*  268 */       break;
/*      */     case PARENT: 
/*  270 */       this.sql.append("mi.id, mi.index_id, mi.type, mi.parent_id");
/*  271 */       break;
/*      */     case MODCONTENT: 
/*  273 */       this.sql.append("mi.id, mi.index_id, mi.type, mi.mod_content");
/*      */     }
/*      */     
/*  276 */     addSortColumn(sort);
/*  277 */     this.sql.append(" FROM ").append(DbMailItem.getMailItemTableName(this.mailbox, "mi", this.dumpster));
/*  278 */     this.sql.append(getForceIndexClause(node, sort, validLIMIT));
/*      */     
/*  280 */     if (joinAppt) {
/*  281 */       this.sql.append(", ").append(DbMailItem.getCalendarItemTableName(this.mailbox, "ap", this.dumpster));
/*      */     }
/*  283 */     if (joinTaggedItem) {
/*  284 */       this.sql.append(", ").append(DbTag.getTaggedItemTableName(this.mailbox, "ti"));
/*      */     }
/*  286 */     this.sql.append(" WHERE ");
/*  287 */     if (!DebugConfig.disableMailboxGroups) {
/*  288 */       this.sql.append("mi.mailbox_id = ? AND ");
/*  289 */       this.params.add(Integer.valueOf(this.mailbox.getId()));
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*  295 */     if ((sort != null) && ((sort.equals(SortBy.RCPT_ASC)) || (sort.equals(SortBy.RCPT_DESC))) && (maybeExcludeHasRecipients)) {
/*  296 */       this.sql.append("(mi.recipients is NOT NULL) AND ");
/*      */     }
/*  298 */     if (joinAppt) {
/*  299 */       if (!DebugConfig.disableMailboxGroups) {
/*  300 */         this.sql.append("mi.mailbox_id = ap.mailbox_id AND ");
/*      */       }
/*  302 */       this.sql.append("mi.id = ap.item_id AND ");
/*      */     }
/*      */   }
/*      */   
/*      */   private static boolean searchingInDrafts(DbSearchConstraints node) {
/*  307 */     if ((node instanceof DbSearchConstraints.Leaf)) {
/*  308 */       for (Folder folder : ((DbSearchConstraints.Leaf)node).folders) {
/*  309 */         if (folder.getId() == 6) {
/*  310 */           return true;
/*      */         }
/*      */       }
/*  313 */       return false;
/*      */     }
/*  315 */     boolean success = false;
/*  316 */     for (DbSearchConstraints child : node.getChildren()) {
/*  317 */       if (searchingInDrafts(child)) {
/*  318 */         success = true;
/*      */       }
/*      */     }
/*  321 */     return success;
/*      */   }
/*      */   
/*      */   private void encodeConstraint(DbSearchConstraints node, byte[] calTypes, boolean inCalTable, boolean joinTaggedItem)
/*      */   {
/*  326 */     if (((node instanceof DbSearchConstraints.Intersection)) || ((node instanceof DbSearchConstraints.Union))) {
/*  327 */       boolean first = true;
/*  328 */       boolean and = node instanceof DbSearchConstraints.Intersection;
/*  329 */       this.sql.append('(');
/*  330 */       for (DbSearchConstraints child : node.getChildren()) {
/*  331 */         if (!child.isEmpty()) {
/*  332 */           if (!first) {
/*  333 */             this.sql.append(and ? " AND " : " OR ");
/*      */           }
/*  335 */           encodeConstraint(child, calTypes, inCalTable, joinTaggedItem);
/*  336 */           first = false;
/*      */         }
/*      */       }
/*  339 */       this.sql.append(") ");
/*  340 */       return;
/*      */     }
/*      */     
/*      */ 
/*  344 */     if (node.isEmpty()) return;
/*  345 */     DbSearchConstraints.Leaf constraint = node.toLeaf();
/*  346 */     assert (((node instanceof DbSearchConstraints.Leaf)) && (constraint != null));
/*      */     
/*      */ 
/*  349 */     if (constraint.noResults) {
/*  350 */       this.sql.append(Db.supports(Db.Capability.BOOLEAN_DATATYPE) ? "FALSE" : "0=1");
/*  351 */       return;
/*      */     }
/*  353 */     this.sql.append('(');
/*  354 */     boolean needAnd = false;
/*      */     
/*  356 */     if (ListUtil.isEmpty(constraint.types)) {
/*  357 */       if (!constraint.typesFactoredOut)
/*      */       {
/*  359 */         this.sql.append("mi.type NOT IN ").append(DbMailItem.NON_SEARCHABLE_TYPES);
/*  360 */         needAnd = true;
/*      */       }
/*      */     } else {
/*  363 */       this.sql.append(DbUtil.whereIn("mi.type", constraint.types.size()));
/*  364 */       for (MailItem.Type type : constraint.types) {
/*  365 */         this.params.add(Byte.valueOf(type.toByte()));
/*      */       }
/*  367 */       needAnd = true;
/*      */     }
/*      */     
/*  370 */     needAnd |= encodeNoRecipients(constraint.excludeHasRecipients, needAnd);
/*  371 */     needAnd |= encodeType(constraint.excludeTypes, false, needAnd);
/*  372 */     needAnd |= encode("mi.type", inCalTable, calTypes, needAnd);
/*  373 */     needAnd |= encodeTag(constraint.tags, true, joinTaggedItem, needAnd);
/*  374 */     needAnd |= encodeTag(constraint.excludeTags, false, false, needAnd);
/*  375 */     needAnd |= encodeFolder(constraint.folders, true, needAnd);
/*  376 */     needAnd |= encodeFolder(constraint.excludeFolders, false, needAnd);
/*      */     
/*  378 */     if (constraint.convId > 0) {
/*  379 */       needAnd |= encode("mi.parent_id", true, Integer.valueOf(constraint.convId), needAnd);
/*      */     } else {
/*  381 */       needAnd |= encode("mi.parent_id", false, constraint.prohibitedConvIds, needAnd);
/*      */     }
/*  383 */     needAnd |= encode("mi.id", true, constraint.itemIds, needAnd);
/*  384 */     needAnd |= encode("mi.id", false, constraint.prohibitedItemIds, needAnd);
/*  385 */     needAnd |= encode("mi.index_id", true, constraint.indexIds, needAnd);
/*  386 */     if (constraint.cursorRange != null) {
/*  387 */       needAnd |= encodeCursorRange(constraint.cursorRange, needAnd);
/*      */     }
/*      */     
/*  390 */     for (Map.Entry<DbSearchConstraints.RangeType, DbSearchConstraints.Range> entry : constraint.ranges.entries()) {
/*  391 */       switch ((DbSearchConstraints.RangeType)entry.getKey()) {
/*      */       case DATE: 
/*  393 */         needAnd |= encodeDateRange("mi.date", (DbSearchConstraints.NumericRange)entry.getValue(), needAnd);
/*  394 */         break;
/*      */       case MDATE: 
/*  396 */         needAnd |= encodeDateRange("mi.change_date", (DbSearchConstraints.NumericRange)entry.getValue(), needAnd);
/*  397 */         break;
/*      */       case MODSEQ: 
/*  399 */         needAnd |= encodeLongRange("mi.mod_metadata", (DbSearchConstraints.NumericRange)entry.getValue(), 1L, needAnd);
/*  400 */         break;
/*      */       case SIZE: 
/*  402 */         needAnd |= encodeLongRange("mi.size", (DbSearchConstraints.NumericRange)entry.getValue(), 0L, needAnd);
/*  403 */         break;
/*      */       case CAL_START_DATE: 
/*  405 */         if (inCalTable) {
/*  406 */           needAnd |= encodeTimestampRange("ap.start_time", (DbSearchConstraints.NumericRange)entry.getValue(), 1L, needAnd);
/*      */         }
/*      */         break;
/*      */       case CAL_END_DATE: 
/*  410 */         if (inCalTable) {
/*  411 */           needAnd |= encodeTimestampRange("ap.end_time", (DbSearchConstraints.NumericRange)entry.getValue(), 1L, needAnd);
/*      */         }
/*      */         break;
/*      */       case SENDER: 
/*  415 */         needAnd |= encodeStringRange("mi.sender", (DbSearchConstraints.StringRange)entry.getValue(), needAnd);
/*  416 */         break;
/*      */       case SUBJECT: 
/*  418 */         needAnd |= encodeStringRange("mi.subject", (DbSearchConstraints.StringRange)entry.getValue(), needAnd);
/*      */       }
/*      */       
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*  426 */     Boolean isSoloPart = node.toLeaf().getIsSoloPart();
/*  427 */     if (isSoloPart != null) {
/*  428 */       if (needAnd) this.sql.append(" AND ");
/*  429 */       needAnd = true;
/*  430 */       if (isSoloPart.booleanValue()) {
/*  431 */         this.sql.append("mi.parent_id is NULL ");
/*      */       } else {
/*  433 */         this.sql.append("mi.parent_id is NOT NULL ");
/*      */       }
/*      */     }
/*      */     
/*  437 */     if (constraint.hasIndexId != null) {
/*  438 */       if (needAnd) this.sql.append(" AND ");
/*  439 */       needAnd = true;
/*  440 */       if (constraint.hasIndexId.booleanValue()) {
/*  441 */         this.sql.append("mi.index_id is NOT NULL ");
/*      */       } else {
/*  443 */         this.sql.append("mi.index_id is NULL ");
/*      */       }
/*      */     }
/*  446 */     this.sql.append(')');
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean hasMailItemOnlyConstraints(DbSearchConstraints node)
/*      */   {
/*  454 */     if (((node instanceof DbSearchConstraints.Intersection)) || ((node instanceof DbSearchConstraints.Union))) {
/*  455 */       for (DbSearchConstraints child : node.getChildren()) {
/*  456 */         if (hasMailItemOnlyConstraints(child)) {
/*  457 */           return true;
/*      */         }
/*      */       }
/*  460 */       return false;
/*      */     }
/*  462 */     return node.toLeaf().hasNonAppointmentTypes();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private boolean hasAppointmentTableConstraints(DbSearchConstraints node)
/*      */   {
/*  469 */     if (((node instanceof DbSearchConstraints.Intersection)) || ((node instanceof DbSearchConstraints.Union))) {
/*  470 */       for (DbSearchConstraints child : node.getChildren()) {
/*  471 */         if (hasAppointmentTableConstraints(child)) {
/*  472 */           return true;
/*      */         }
/*      */       }
/*  475 */       return false;
/*      */     }
/*  477 */     return node.toLeaf().hasAppointmentTableConstraints();
/*      */   }
/*      */   
/*      */   private static List<Result> intersectSortedLists(List<Result> toRet, List<List<Result>> lists) {
/*  481 */     if (lists.size() < 0) {
/*  482 */       return toRet;
/*      */     }
/*      */     
/*  485 */     Collections.sort(lists, new Comparator()
/*      */     {
/*      */       public int compare(List<DbSearch.Result> l1, List<DbSearch.Result> l2) {
/*  488 */         return l1.size() - l2.size();
/*      */       }
/*      */     });
/*      */     
/*  492 */     for (Result result : (List)lists.get(0)) {
/*  493 */       boolean intersect = true;
/*  494 */       for (int i = 1; i < lists.size(); i++) {
/*  495 */         if (!((List)lists.get(i)).contains(result)) {
/*  496 */           intersect = false;
/*  497 */           break;
/*      */         }
/*      */       }
/*  500 */       if (intersect) {
/*  501 */         toRet.add(result);
/*      */       }
/*      */     }
/*  504 */     return toRet;
/*      */   }
/*      */   
/*      */   public List<Result> search(DbPool.DbConnection conn, DbSearchConstraints node, SortBy sort, int offset, int limit, FetchMode fetch) throws ServiceException
/*      */   {
/*  509 */     return search(conn, node, sort, offset, limit, fetch, true);
/*      */   }
/*      */   
/*      */   private List<Result> search(DbPool.DbConnection conn, DbSearchConstraints node, SortBy sort, int offset, int limit, FetchMode fetch, boolean searchDraftsSeparately) throws ServiceException
/*      */   {
/*  514 */     if ((!Db.supports(Db.Capability.AVOID_OR_IN_WHERE_CLAUSE)) || (!(node instanceof DbSearchConstraints.Union))) {
/*      */       try {
/*  516 */         node = node.optimize();
/*  517 */         return searchInternal(conn, node, sort, offset, limit, fetch, searchDraftsSeparately);
/*      */       } catch (SQLException e) {
/*  519 */         if (Db.errorMatches(e, Db.Error.TOO_MANY_SQL_PARAMS)) {
/*  520 */           ZimbraLog.sqltrace.debug("Too many SQL params: %s", node, e);
/*      */         } else {
/*  522 */           throw ServiceException.FAILURE("Failed to search", e);
/*      */         }
/*      */       }
/*      */     }
/*  526 */     List<Result> result = new ArrayList();
/*  527 */     if (!(node instanceof DbSearchConstraints.Leaf))
/*      */     {
/*      */ 
/*  530 */       if ((node instanceof DbSearchConstraints.Union)) {
/*  531 */         for (DbSearchConstraints child : node.getChildren()) {
/*  532 */           result.addAll(new DbSearch(this.mailbox, this.dumpster).search(conn, child, sort, offset, limit, fetch));
/*      */         }
/*  534 */         Collections.sort(result, new ResultComparator(sort));
/*  535 */       } else if ((node instanceof DbSearchConstraints.Intersection)) {
/*  536 */         List<List<Result>> resultLists = new ArrayList();
/*      */         
/*  538 */         for (DbSearchConstraints child : node.getChildren()) {
/*  539 */           resultLists.add(new DbSearch(this.mailbox, this.dumpster).search(conn, child, sort, offset, limit, fetch));
/*      */         }
/*  541 */         result = intersectSortedLists(result, resultLists);
/*      */       } else {
/*  543 */         throw ServiceException.FAILURE("Reached merge/intersect block with something other than OR/AND clause", null);
/*      */       }
/*      */     }
/*      */     else
/*      */     {
/*  548 */       DbSearchConstraints.Leaf leafNode = node.toLeaf();
/*  549 */       int dbLimit = Db.getInstance().getParamLimit();
/*      */       
/*  551 */       int otherConstraintsCount = this.params.size() - leafNode.folders.size();
/*  552 */       int softLimit = dbLimit - otherConstraintsCount - 10;
/*  553 */       if (leafNode.folders.size() > softLimit) {
/*  554 */         List<Folder> folderList = new ArrayList(leafNode.folders);
/*  555 */         int end = leafNode.folders.size();
/*  556 */         int start = end - softLimit;
/*  557 */         leafNode.folders.clear();
/*  558 */         while (start > 0) {
/*  559 */           DbSearchConstraints.Leaf subsetNode = leafNode.clone();
/*  560 */           List<Folder> subList = folderList.subList(start, end);
/*  561 */           subsetNode.folders.addAll(subList);
/*  562 */           result.addAll(new DbSearch(this.mailbox, this.dumpster).search(conn, subsetNode, sort, offset, limit, fetch));
/*  563 */           end -= softLimit;
/*  564 */           start -= softLimit;
/*      */         }
/*      */         
/*  567 */         DbSearchConstraints.Leaf subsetNode = leafNode.clone();
/*  568 */         List<Folder> subList = folderList.subList(0, end);
/*  569 */         subsetNode.folders.addAll(subList);
/*  570 */         result.addAll(new DbSearch(this.mailbox, this.dumpster).search(conn, subsetNode, sort, offset, limit, fetch));
/*  571 */         Collections.sort(result, new ResultComparator(sort));
/*      */       } else {
/*  573 */         throw ServiceException.FAILURE("splitting failed, too many constraints but not caused entirely by folders", null);
/*      */       }
/*      */     }
/*      */     
/*  577 */     return result;
/*      */   }
/*      */   
/*      */   private int countUnread(Folder folder) throws ServiceException {
/*  581 */     int count = folder.getUnreadCount();
/*  582 */     List<Folder> subFolders = folder.getSubfolders(null);
/*  583 */     if (subFolders.size() > 0) {
/*  584 */       for (Folder f : subFolders) {
/*  585 */         count += countUnread(f);
/*      */       }
/*      */     }
/*  588 */     return count;
/*      */   }
/*      */   
/*      */   private List<Result> searchInternal(DbPool.DbConnection conn, DbSearchConstraints node, SortBy sort, int offset, int limit, FetchMode fetch, boolean searchDraftsSeparately)
/*      */     throws SQLException, ServiceException
/*      */   {
/*  594 */     if ((searchingInDrafts(node)) && (searchDraftsSeparately) && (sort != null) && ((sort.equals(SortBy.RCPT_ASC)) || (sort.equals(SortBy.RCPT_DESC)))) {
/*  595 */       DbSearchConstraints.Leaf draftsConstraint = findDraftsConstraint(node).clone();
/*  596 */       for (Folder folder : draftsConstraint.folders) {
/*  597 */         if (folder.getId() == 6) {
/*  598 */           draftsConstraint.folders.clear();
/*  599 */           draftsConstraint.folders.add(folder);
/*  600 */           break;
/*      */         }
/*      */       }
/*  603 */       draftsConstraint.excludeHasRecipients = true;
/*      */       DbSearchConstraints node2;
/*      */       DbSearchConstraints node1;
/*  606 */       DbSearchConstraints node2; if (sort.equals(SortBy.RCPT_ASC)) {
/*  607 */         DbSearchConstraints node1 = node;
/*  608 */         node2 = draftsConstraint;
/*      */       } else {
/*  610 */         node1 = draftsConstraint;
/*  611 */         node2 = node;
/*      */       }
/*      */       
/*  614 */       List<Result> result = searchTwoConstraints(conn, node1, node2, sort, offset, limit, fetch);
/*  615 */       return result;
/*      */     }
/*      */     
/*      */ 
/*  619 */     boolean hasValidLIMIT = (offset >= 0) && (limit >= 0);
/*  620 */     boolean hasMailItemOnlyConstraints = true;
/*  621 */     boolean hasAppointmentTableConstraints = hasAppointmentTableConstraints(node);
/*  622 */     if (hasAppointmentTableConstraints) {
/*  623 */       hasMailItemOnlyConstraints = hasMailItemOnlyConstraints(node);
/*      */     }
/*  625 */     boolean requiresUnion = (hasMailItemOnlyConstraints) && (hasAppointmentTableConstraints);
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  632 */     boolean joinTaggedItem = false;
/*  633 */     if (((node instanceof DbSearchConstraints.Leaf)) && (!this.dumpster)) {
/*  634 */       DbSearchConstraints.Leaf constraint = node.toLeaf();
/*  635 */       if ((constraint.excludeTags.isEmpty()) && (!constraint.tags.isEmpty()) && (constraint.tags.size() == 1)) {
/*  636 */         Tag tag = (Tag)constraint.tags.iterator().next();
/*  637 */         if ((tag.getId() == Flag.FlagInfo.UNREAD.toId()) || (tag.getId() > 0)) {
/*  638 */           long count = 0L;
/*  639 */           if (tag.getId() == Flag.FlagInfo.UNREAD.toId())
/*      */           {
/*      */ 
/*      */ 
/*  643 */             count = countUnread(this.mailbox.getFolderById(null, 1));
/*  644 */           } else if (tag.getId() > 0) {
/*  645 */             count = tag.getSize();
/*      */           }
/*      */           
/*  648 */           if (count < LC.search_tagged_item_count_join_query_cutoff.intValue()) {
/*  649 */             joinTaggedItem = true;
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*  654 */     if (hasMailItemOnlyConstraints) {
/*  655 */       if (requiresUnion) {
/*  656 */         this.sql.append('(');
/*      */       }
/*  658 */       boolean maybeExcludeNoRecipients = true;
/*  659 */       if (node.toLeaf() != null) {
/*  660 */         maybeExcludeNoRecipients = !node.toLeaf().excludeHasRecipients;
/*      */       }
/*      */       
/*      */ 
/*  664 */       encodeSelect(sort, fetch, false, joinTaggedItem, node, hasValidLIMIT, maybeExcludeNoRecipients);
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  674 */       encodeConstraint(node, hasAppointmentTableConstraints ? APPOINTMENT_TABLE_TYPES : null, false, joinTaggedItem);
/*      */       
/*  676 */       if (requiresUnion) {
/*  677 */         this.sql.append(orderBy(sort, true));
/*      */         
/*  679 */         if ((hasValidLIMIT) && (Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  680 */           this.sql.append(' ').append(Db.getInstance().limit(offset, limit));
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*  685 */     if (requiresUnion) {
/*  686 */       this.sql.append(" ) UNION ALL (");
/*      */     }
/*      */     
/*  689 */     if (hasAppointmentTableConstraints)
/*      */     {
/*  691 */       encodeSelect(sort, fetch, true, false, node, hasValidLIMIT);
/*  692 */       encodeConstraint(node, APPOINTMENT_TABLE_TYPES, true, false);
/*  693 */       if (requiresUnion) {
/*  694 */         this.sql.append(orderBy(sort, true));
/*      */         
/*  696 */         if ((hasValidLIMIT) && (Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  697 */           this.sql.append(' ').append(Db.getInstance().limit(offset, limit));
/*      */         }
/*  699 */         if (requiresUnion) {
/*  700 */           this.sql.append(')');
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*  706 */     this.sql.append(orderBy(sort, true));
/*      */     
/*      */ 
/*  709 */     if ((hasValidLIMIT) && (Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  710 */       this.sql.append(' ').append(Db.getInstance().limit(offset, limit));
/*      */     }
/*      */     
/*  713 */     if (Db.supports(Db.Capability.SQL_PARAM_LIMIT)) {
/*  714 */       Db.getInstance().checkParamLimit(this.params.size());
/*      */     }
/*  716 */     PreparedStatement stmt = null;
/*  717 */     ResultSet rs = null;
/*      */     try
/*      */     {
/*  720 */       stmt = conn.prepareStatement(this.sql.toString());
/*  721 */       setParameters(stmt);
/*      */       
/*  723 */       if ((hasValidLIMIT) && (!Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  724 */         stmt.setMaxRows(offset + limit + 1);
/*      */       }
/*  726 */       rs = stmt.executeQuery();
/*      */       
/*  728 */       List<Result> result = new ArrayList();
/*  729 */       Object sortkey; while (rs.next())
/*  730 */         if ((hasValidLIMIT) && (!Db.supports(Db.Capability.LIMIT_CLAUSE))) {
/*  731 */           if (offset-- <= 0)
/*      */           {
/*      */ 
/*  734 */             if (limit-- <= 0) {
/*      */               break;
/*      */             }
/*      */           }
/*      */         } else {
/*  739 */           sortkey = getSortKey(rs, sort);
/*  740 */           switch (fetch) {
/*      */           case ID: 
/*  742 */             result.add(new IdResult(rs, sortkey));
/*  743 */             break;
/*      */           case MAIL_ITEM: 
/*  745 */             result.add(new ItemDataResult(rs, sortkey, this.dumpster));
/*  746 */             break;
/*      */           case IMAP_MSG: 
/*  748 */             result.add(new ImapResult(rs, sortkey));
/*  749 */             break;
/*      */           case MODSEQ: 
/*  751 */             result.add(new ModSeqResult(rs, sortkey));
/*  752 */             break;
/*      */           case MODCONTENT: 
/*  754 */             result.add(new ModContentResult(rs, sortkey));
/*  755 */             break;
/*      */           case PARENT: 
/*  757 */             result.add(new ParentResult(rs, sortkey));
/*  758 */             break;
/*      */           default: 
/*  760 */             if (!$assertionsDisabled) throw new AssertionError(fetch);
/*      */             break; }
/*      */         }
/*  763 */       return result;
/*      */     } finally {
/*  765 */       conn.closeQuietly(rs);
/*  766 */       conn.closeQuietly(stmt);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private List<Result> searchTwoConstraints(DbPool.DbConnection conn, DbSearchConstraints node1, DbSearchConstraints node2, SortBy sort, int offset, int limit, FetchMode fetch)
/*      */     throws SQLException, ServiceException
/*      */   {
/*  787 */     List<Result> result1 = new DbSearch(this.mailbox, this.dumpster).search(conn, node1, sort, offset, limit, fetch, false);
/*  788 */     if (result1.size() == 0)
/*      */     {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  795 */       boolean ignoreNoRecipients = true;
/*  796 */       if (node1.toLeaf() != null) {
/*  797 */         ignoreNoRecipients = !node1.toLeaf().excludeHasRecipients;
/*      */       }
/*  799 */       int offset2 = offset - new DbSearch(this.mailbox, this.dumpster).countResults(conn, node1, ignoreNoRecipients);
/*  800 */       int limit2 = limit;
/*  801 */       List<Result> result2 = new DbSearch(this.mailbox, this.dumpster).search(conn, node2, sort, offset2, limit2, fetch, false);
/*  802 */       result1.addAll(result2);
/*      */     }
/*  804 */     else if (result1.size() < limit)
/*      */     {
/*      */ 
/*      */ 
/*      */ 
/*  809 */       int offset2 = 0;
/*  810 */       int limit2 = limit - result1.size();
/*  811 */       List<Result> result2 = new DbSearch(this.mailbox, this.dumpster).search(conn, node2, sort, offset2, limit2, fetch, false);
/*  812 */       result1.addAll(result2);
/*      */     }
/*  814 */     return result1;
/*      */   }
/*      */   
/*      */   private static DbSearchConstraints.Leaf findDraftsConstraint(DbSearchConstraints node)
/*      */   {
/*  819 */     if ((node instanceof DbSearchConstraints.Leaf)) {
/*  820 */       for (Folder folder : ((DbSearchConstraints.Leaf)node).folders) {
/*  821 */         if (folder.getId() == 6) {
/*  822 */           return node.toLeaf();
/*      */         }
/*      */       }
/*      */     } else {
/*  826 */       for (DbSearchConstraints child : node.getChildren()) {
/*  827 */         DbSearchConstraints.Leaf drafts = findDraftsConstraint(child);
/*  828 */         if (drafts != null) {
/*  829 */           return drafts;
/*      */         }
/*      */       }
/*      */     }
/*  833 */     return null;
/*      */   }
/*      */   
/*      */   private static Object getSortKey(ResultSet rs, SortBy sort) throws SQLException {
/*  837 */     switch (sort.getKey()) {
/*      */     case NONE: 
/*  839 */       return null;
/*      */     case SENDER: 
/*      */     case RCPT: 
/*      */     case SUBJECT: 
/*      */     case NAME: 
/*      */     case NAME_NATURAL_ORDER: 
/*      */     case ATTACHMENT: 
/*      */     case FLAG: 
/*      */     case PRIORITY: 
/*  848 */       return Strings.nullToEmpty(rs.getString("sortcol"));
/*      */     case SIZE: 
/*  850 */       return Long.valueOf(rs.getInt("sortcol"));
/*      */     }
/*      */     
/*  853 */     return Long.valueOf(rs.getInt("sortcol") * 1000L);
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, Object o)
/*      */   {
/*  858 */     return encode(column, bool, o, true);
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, Object o, boolean and) {
/*  862 */     if (and) this.sql.append(" AND ");
/*  863 */     this.sql.append(column).append(bool ? " = ?" : " != ?");
/*  864 */     this.params.add(o);
/*  865 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encodeFolder(Set<Folder> folders, boolean bool) {
/*  869 */     return encodeFolder(folders, bool, true);
/*      */   }
/*      */   
/*      */   private boolean encodeFolder(Set<Folder> folders, boolean bool, boolean and) {
/*  873 */     if (folders.isEmpty()) {
/*  874 */       return false;
/*      */     }
/*  876 */     if (and) this.sql.append(" AND ");
/*  877 */     this.sql.append(DbUtil.whereIn("mi.folder_id", bool, folders.size()));
/*  878 */     for (Folder folder : folders) {
/*  879 */       this.params.add(Integer.valueOf(folder.getId()));
/*      */     }
/*  881 */     return true;
/*      */   }
/*      */   
/*      */ 
/*  885 */   private boolean encodeType(Set<MailItem.Type> types, boolean bool) { return encodeType(types, bool, true); }
/*      */   
/*      */   private boolean encodeType(Set<MailItem.Type> types, boolean bool, boolean and) {
/*  888 */     if (types.isEmpty()) {
/*  889 */       return false;
/*      */     }
/*  891 */     if (and) this.sql.append(" AND ");
/*  892 */     this.sql.append(DbUtil.whereIn("type", bool, types.size()));
/*  893 */     for (MailItem.Type type : types) {
/*  894 */       this.params.add(Byte.valueOf(type.toByte()));
/*      */     }
/*  896 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, Collection<?> c) {
/*  900 */     return encode(column, bool, c, true);
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, Collection<?> c, boolean and) {
/*  904 */     if (ListUtil.isEmpty(c)) {
/*  905 */       return false;
/*      */     }
/*  907 */     if (and) this.sql.append(" AND ");
/*  908 */     this.sql.append(DbUtil.whereIn(column, bool, c.size()));
/*  909 */     this.params.addAll(c);
/*  910 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, byte[] array) {
/*  914 */     return encode(column, bool, array, true);
/*      */   }
/*      */   
/*      */   private boolean encode(String column, boolean bool, byte[] array, boolean and) {
/*  918 */     if ((array == null) || (array.length == 0)) {
/*  919 */       return false;
/*      */     }
/*  921 */     if (and) this.sql.append(" AND ");
/*  922 */     this.sql.append(DbUtil.whereIn(column, bool, array.length));
/*  923 */     for (byte b : array) {
/*  924 */       this.params.add(Byte.valueOf(b));
/*      */     }
/*  926 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encodeNoRecipients(boolean excludeHasRecipients) {
/*  930 */     return encodeNoRecipients(excludeHasRecipients, true);
/*      */   }
/*      */   
/*      */   private boolean encodeNoRecipients(boolean excludeHasRecipients, boolean and) {
/*  934 */     if (excludeHasRecipients) {
/*  935 */       if (and) this.sql.append(" AND ");
/*  936 */       this.sql.append("mi.recipients is NULL");
/*  937 */       return true;
/*      */     }
/*  939 */     return false;
/*      */   }
/*      */   
/*      */   private boolean encodeTag(Set<Tag> tags, boolean bool, boolean useJoin)
/*      */   {
/*  944 */     return encodeTag(tags, bool, useJoin, true);
/*      */   }
/*      */   
/*      */   private boolean encodeTag(Set<Tag> tags, boolean bool, boolean useJoin, boolean and) {
/*  948 */     if (tags.isEmpty()) {
/*  949 */       return false;
/*      */     }
/*      */     
/*  952 */     if (useJoin) {
/*  953 */       assert (!this.dumpster);
/*  954 */       assert (bool);
/*      */     }
/*      */     
/*  957 */     if (this.dumpster) {
/*  958 */       int flags = 0;
/*  959 */       for (Tag tag : tags) {
/*  960 */         if ((tag instanceof Flag)) {
/*  961 */           Flag flag = (Flag)tag;
/*  962 */           if (flag.getId() == Flag.ID_UNREAD) {
/*  963 */             if (and) this.sql.append(" AND ");
/*  964 */             this.sql.append("mi.unread = ?");
/*  965 */             and = true;
/*  966 */             this.params.add(Integer.valueOf(bool ? 1 : 0));
/*      */           } else {
/*  968 */             flags |= flag.toBitmask();
/*      */           }
/*      */         } else {
/*  971 */           if (and) this.sql.append(" AND ");
/*  972 */           and = true;
/*  973 */           this.sql.append("(mi.tag_names");
/*  974 */           if (!bool) {
/*  975 */             this.sql.append(" IS NULL OR mi.tag_names NOT");
/*      */           }
/*  977 */           this.sql.append(" LIKE ?)");
/*  978 */           this.params.add(DbTag.tagLIKEPattern(tag.getName()));
/*      */         }
/*      */       }
/*  981 */       if (flags != 0) {
/*  982 */         this.sql.append(" AND ").append(Db.getInstance().bitAND("mi.flags", "?")).append(" = ?");
/*  983 */         this.params.add(Integer.valueOf(flags));
/*  984 */         this.params.add(Integer.valueOf(bool ? flags : 0));
/*      */       }
/*  986 */     } else if (bool) {
/*  987 */       if (useJoin) {
/*  988 */         assert (tags.size() == 1);
/*      */         
/*  990 */         Tag tag = (Tag)tags.iterator().next();
/*      */         
/*      */ 
/*  993 */         if (and) this.sql.append(" AND ");
/*  994 */         this.sql.append("(mi.id = ti.item_id AND mi.mailbox_id = ti.mailbox_id AND ti.tag_id = ?)");
/*  995 */         this.params.add(Integer.valueOf(tag.getId()));
/*  996 */         and = true;
/*      */       } else {
/*  998 */         for (Tag tag : tags) {
/*  999 */           if (and) this.sql.append(" AND ");
/* 1000 */           this.sql.append("EXISTS (SELECT * FROM ").append(DbTag.getTaggedItemTableName(this.mailbox, "ti"));
/* 1001 */           and = true;
/* 1002 */           this.sql.append(" WHERE ");
/* 1003 */           if (!DebugConfig.disableMailboxGroups) {
/* 1004 */             this.sql.append("mi.mailbox_id = ti.mailbox_id AND ");
/*      */           }
/* 1006 */           this.sql.append("mi.id = ti.item_id AND ti.tag_id = ?)");
/* 1007 */           this.params.add(Integer.valueOf(tag.getId()));
/*      */         }
/*      */       }
/*      */     } else {
/* 1011 */       if (and) this.sql.append(" AND ");
/* 1012 */       this.sql.append("NOT EXISTS (SELECT * FROM ").append(DbTag.getTaggedItemTableName(this.mailbox, "ti"));
/* 1013 */       and = true;
/* 1014 */       this.sql.append(" WHERE ");
/* 1015 */       if (!DebugConfig.disableMailboxGroups) {
/* 1016 */         this.sql.append("mi.mailbox_id = ti.mailbox_id AND ");
/*      */       }
/* 1018 */       this.sql.append("mi.id = ti.item_id AND ").append(DbUtil.whereIn("ti.tag_id", tags.size())).append(')');
/* 1019 */       for (Tag tag : tags) {
/* 1020 */         this.params.add(Integer.valueOf(tag.getId()));
/*      */       }
/*      */     }
/* 1023 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encodeDateRange(String column, DbSearchConstraints.NumericRange range) {
/* 1027 */     return encodeDateRange(column, range, true);
/*      */   }
/*      */   
/*      */   private boolean encodeTimestampRange(String column, DbSearchConstraints.NumericRange range, long cutoff) {
/* 1031 */     return encodeTimestampRange(column, range, cutoff, true);
/*      */   }
/*      */   
/*      */   private boolean encodeLongRange(String column, DbSearchConstraints.NumericRange range, long cutoff) {
/* 1035 */     return encodeLongRange(column, range, cutoff, true);
/*      */   }
/*      */   
/*      */   private boolean encodeIntRange(String column, DbSearchConstraints.NumericRange range, int cutoff) {
/* 1039 */     return encodeIntRange(column, range, cutoff, true);
/*      */   }
/*      */   
/*      */   private boolean encodeDateRange(String column, DbSearchConstraints.NumericRange range, boolean and) {
/* 1043 */     return encodeRange(column, range, 1L, Integer.valueOf((int)Math.min(range.min / 1000L, 2147483647L)), Integer.valueOf((int)Math.min(range.max / 1000L, 2147483647L)), and);
/*      */   }
/*      */   
/*      */   private boolean encodeTimestampRange(String column, DbSearchConstraints.NumericRange range, long cutoff, boolean and)
/*      */   {
/* 1048 */     return encodeRange(column, range, cutoff, new Timestamp(range.min), new Timestamp(range.max), and);
/*      */   }
/*      */   
/*      */   private boolean encodeLongRange(String column, DbSearchConstraints.NumericRange range, long cutoff, boolean and) {
/* 1052 */     return encodeRange(column, range, cutoff, Long.valueOf(range.min), Long.valueOf(range.max), and);
/*      */   }
/*      */   
/*      */   private boolean encodeIntRange(String column, DbSearchConstraints.NumericRange range, int cutoff, boolean and) {
/* 1056 */     return encodeRange(column, range, cutoff, Integer.valueOf((int)range.min), Integer.valueOf((int)range.max), and);
/*      */   }
/*      */   
/*      */   private boolean encodeRange(String column, DbSearchConstraints.NumericRange range, long cutoff, Object min, Object max)
/*      */   {
/* 1061 */     return encodeRange(column, range, cutoff, min, max, true);
/*      */   }
/*      */   
/*      */   private boolean encodeRange(String column, DbSearchConstraints.NumericRange range, long cutoff, Object min, Object max, boolean and)
/*      */   {
/* 1066 */     if ((Db.supports(Db.Capability.CASE_SENSITIVE_COMPARISON)) && (isCaseSensitiveField(column))) {
/* 1067 */       column = "UPPER(" + column + ")";
/*      */     }
/* 1069 */     boolean lowValid = range.min >= cutoff;
/* 1070 */     boolean highValid = range.max >= cutoff;
/* 1071 */     if ((!lowValid) && (!highValid)) {
/* 1072 */       return false;
/*      */     }
/* 1074 */     if (and) this.sql.append(" AND ");
/* 1075 */     this.sql.append(range.bool ? "(" : "NOT (");
/* 1076 */     and = true;
/* 1077 */     if (lowValid) {
/* 1078 */       this.sql.append(column).append(range.minInclusive ? " >= ?" : " > ?");
/* 1079 */       this.params.add(min);
/*      */     }
/* 1081 */     if (highValid) {
/* 1082 */       if (lowValid) {
/* 1083 */         this.sql.append(" AND ");
/*      */       }
/* 1085 */       this.sql.append(column).append(range.maxInclusive ? " <= ?" : " < ?");
/* 1086 */       this.params.add(max);
/*      */     }
/* 1088 */     this.sql.append(')');
/* 1089 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encodeStringRange(String column, DbSearchConstraints.StringRange range) {
/* 1093 */     return encodeStringRange(column, range, true);
/*      */   }
/*      */   
/*      */   private boolean encodeStringRange(String column, DbSearchConstraints.StringRange range, boolean and) {
/* 1097 */     if ((Db.supports(Db.Capability.CASE_SENSITIVE_COMPARISON)) && (isCaseSensitiveField(column))) {
/* 1098 */       column = "UPPER(" + column + ")";
/*      */     }
/* 1100 */     if (and) this.sql.append(" AND ");
/* 1101 */     this.sql.append(range.bool ? "(" : "NOT (");
/* 1102 */     if (range.min != null) {
/* 1103 */       this.sql.append(column).append(range.minInclusive ? " >= ?" : " > ?");
/* 1104 */       this.params.add(range.min.replace("\\\"", "\""));
/*      */     }
/* 1106 */     if (range.max != null) {
/* 1107 */       if (range.min != null) {
/* 1108 */         this.sql.append(" AND ");
/*      */       }
/* 1110 */       this.sql.append(column).append(range.maxInclusive ? " <= ?" : " < ?");
/* 1111 */       this.params.add(range.max.replace("\\\"", "\""));
/*      */     }
/* 1113 */     this.sql.append(')');
/* 1114 */     return true;
/*      */   }
/*      */   
/*      */   private boolean encodeCursorRange(DbSearchConstraints.CursorRange range) {
/* 1118 */     return encodeCursorRange(range, true);
/*      */   }
/*      */   
/*      */   private boolean encodeCursorRange(DbSearchConstraints.CursorRange range, boolean and)
/*      */   {
/* 1123 */     String col = toSortField(range.sortBy);
/* 1124 */     if (and) this.sql.append(" AND ");
/* 1125 */     this.sql.append("(");
/* 1126 */     if (range.min != null) {
/* 1127 */       this.sql.append(col).append(range.minInclusive ? " >= ?" : " > ?");
/* 1128 */       this.params.add(range.min.replace("\\\"", "\""));
/*      */     }
/* 1130 */     if (range.max != null) {
/* 1131 */       if (range.min != null) {
/* 1132 */         this.sql.append(" AND ");
/*      */       }
/* 1134 */       this.sql.append(col).append(range.maxInclusive ? " <= ?" : " < ?");
/* 1135 */       this.params.add(range.max.replace("\\\"", "\""));
/*      */     }
/* 1137 */     this.sql.append(')');
/* 1138 */     return true;
/*      */   }
/*      */   
/*      */   private void setParameters(PreparedStatement stmt) throws SQLException {
/* 1142 */     int pos = 0;
/* 1143 */     for (Object param : this.params) {
/* 1144 */       stmt.setObject(++pos, param);
/*      */     }
/*      */   }
/*      */   
/*      */   public static abstract class Result
/*      */   {
/*      */     private final Object sortValue;
/*      */     
/*      */     protected Result(Object sortValue) {
/* 1153 */       assert ((sortValue == null) || ((sortValue instanceof String)) || ((sortValue instanceof Long)) || ((sortValue instanceof Integer))) : sortValue;
/* 1154 */       this.sortValue = sortValue;
/*      */     }
/*      */     
/*      */ 
/* 1158 */     public Object getSortValue() { return this.sortValue; }
/*      */     
/*      */     public abstract int getId();
/*      */     
/*      */     public abstract int getIndexId();
/*      */     
/*      */     public abstract MailItem.Type getType();
/*      */     
/* 1166 */     public MailItem.UnderlyingData getItemData() { throw new IllegalStateException(); }
/*      */     
/*      */     public MailItem getItem()
/*      */     {
/* 1170 */       throw new IllegalStateException();
/*      */     }
/*      */     
/*      */     public ImapMessage getImapMessage() {
/* 1174 */       throw new IllegalStateException();
/*      */     }
/*      */     
/*      */     public int getModSeq() {
/* 1178 */       throw new IllegalStateException();
/*      */     }
/*      */     
/*      */     public int getModContent() {
/* 1182 */       throw new IllegalStateException();
/*      */     }
/*      */     
/*      */     public int getParentId() {
/* 1186 */       throw new IllegalStateException();
/*      */     }
/*      */     
/*      */     public String toString()
/*      */     {
/* 1191 */       return Objects.toStringHelper(this).add("id", getId()).add("type", getType()).add("sort", this.sortValue).toString();
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     public int hashCode()
/*      */     {
/* 1200 */       return getId();
/*      */     }
/*      */     
/*      */     public boolean equals(Object obj)
/*      */     {
/* 1205 */       return ((obj instanceof Result)) && (((Result)obj).getId() == getId());
/*      */     }
/*      */   }
/*      */   
/*      */   private static class IdResult extends DbSearch.Result {
/*      */     private final int id;
/*      */     private final int indexId;
/*      */     private final MailItem.Type type;
/*      */     
/*      */     IdResult(ResultSet rs, Object sortkey) throws SQLException {
/* 1215 */       super();
/* 1216 */       this.id = rs.getInt(1);
/* 1217 */       this.indexId = getInt(rs, 2);
/* 1218 */       this.type = MailItem.Type.of(rs.getByte(3));
/*      */     }
/*      */     
/*      */     public int getId()
/*      */     {
/* 1223 */       return this.id;
/*      */     }
/*      */     
/*      */     public int getIndexId()
/*      */     {
/* 1228 */       return this.indexId;
/*      */     }
/*      */     
/*      */     public MailItem.Type getType()
/*      */     {
/* 1233 */       return this.type;
/*      */     }
/*      */     
/*      */ 
/*      */     int getInt(ResultSet rs, int offset)
/*      */       throws SQLException
/*      */     {
/* 1240 */       int value = rs.getInt(offset);
/* 1241 */       return rs.wasNull() ? -1 : value;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ItemDataResult
/*      */     extends DbSearch.Result
/*      */   {
/*      */     private final MailItem.UnderlyingData udata;
/*      */     
/*      */     ItemDataResult(ResultSet rs, Object sortkey, boolean inDumpster)
/*      */       throws ServiceException, SQLException
/*      */     {
/* 1253 */       super();
/* 1254 */       this.udata = DbMailItem.constructItem(rs, 0, inDumpster);
/*      */     }
/*      */     
/*      */     public int getId()
/*      */     {
/* 1259 */       return this.udata.id;
/*      */     }
/*      */     
/*      */     public int getIndexId()
/*      */     {
/* 1264 */       return this.udata.indexId;
/*      */     }
/*      */     
/*      */     public MailItem.Type getType()
/*      */     {
/* 1269 */       return MailItem.Type.of(this.udata.type);
/*      */     }
/*      */     
/*      */     public MailItem.UnderlyingData getItemData()
/*      */     {
/* 1274 */       return this.udata;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ImapResult extends DbSearch.IdResult {
/*      */     private final ImapMessage i4msg;
/*      */     
/*      */     ImapResult(ResultSet rs, Object sortkey) throws SQLException {
/* 1282 */       super(sortkey);
/* 1283 */       int offset = 3;
/* 1284 */       int flags = rs.getBoolean(offset + 2) ? Flag.BITMASK_UNREAD | rs.getInt(offset + 3) : rs.getInt(offset + 3);
/* 1285 */       this.i4msg = new ImapMessage(getId(), getType(), rs.getInt(offset + 1), flags, DbTag.deserializeTags(rs.getString(offset + 4)));
/*      */     }
/*      */     
/*      */     public ImapMessage getImapMessage()
/*      */     {
/* 1290 */       return this.i4msg;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ModSeqResult extends DbSearch.IdResult {
/*      */     private final int modseq;
/*      */     
/*      */     ModSeqResult(ResultSet rs, Object sortkey) throws SQLException {
/* 1298 */       super(sortkey);
/* 1299 */       this.modseq = getInt(rs, 4);
/*      */     }
/*      */     
/*      */     public int getModSeq()
/*      */     {
/* 1304 */       return this.modseq;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ModContentResult extends DbSearch.IdResult {
/*      */     private final int modc;
/*      */     
/*      */     ModContentResult(ResultSet rs, Object sortkey) throws SQLException {
/* 1312 */       super(sortkey);
/* 1313 */       this.modc = getInt(rs, 4);
/*      */     }
/*      */     
/*      */     public int getModContent()
/*      */     {
/* 1318 */       return this.modc;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ParentResult extends DbSearch.IdResult {
/*      */     private final int parentId;
/*      */     
/*      */     ParentResult(ResultSet rs, Object sortkey) throws SQLException {
/* 1326 */       super(sortkey);
/* 1327 */       this.parentId = getInt(rs, 4);
/*      */     }
/*      */     
/*      */     public int getParentId()
/*      */     {
/* 1332 */       return this.parentId;
/*      */     }
/*      */   }
/*      */   
/*      */   private static final class ResultComparator implements Comparator<DbSearch.Result> {
/*      */     private final SortBy sort;
/*      */     
/*      */     ResultComparator(SortBy sort) {
/* 1340 */       this.sort = sort;
/*      */     }
/*      */     
/*      */     public int compare(DbSearch.Result o1, DbSearch.Result o2)
/*      */     {
/* 1345 */       switch (DbSearch.2.$SwitchMap$com$zimbra$cs$index$SortBy$Key[this.sort.getKey().ordinal()]) {
/*      */       case 8: 
/*      */       case 12: 
/* 1348 */         long date1 = ((Long)o1.getSortValue()).longValue();
/* 1349 */         long date2 = ((Long)o2.getSortValue()).longValue();
/* 1350 */         if (date1 != date2) { long diff;
/*      */           long diff;
/* 1352 */           if (this.sort.getDirection() == SortBy.Direction.DESC) {
/* 1353 */             diff = date2 - date1;
/*      */           } else {
/* 1355 */             diff = date1 - date2;
/*      */           }
/* 1357 */           return diff > 0L ? 1 : -1;
/*      */         }
/*      */         
/*      */         break;
/*      */       case 2: 
/*      */       case 4: 
/*      */       case 5: 
/*      */       case 6: 
/* 1365 */         String s1 = (String)o1.getSortValue();
/* 1366 */         String s2 = (String)o2.getSortValue();
/* 1367 */         if (!StringUtil.equal(s1, s2)) {
/* 1368 */           if (this.sort.getDirection() == SortBy.Direction.DESC) {
/* 1369 */             return StringUtil.compareTo(s2, s1);
/*      */           }
/* 1371 */           return StringUtil.compareTo(s1, s2);
/*      */         }
/*      */         break;
/*      */       case 1: case 7: 
/*      */         break;
/*      */       case 3: case 9: 
/*      */       case 10: case 11: 
/*      */       default: 
/* 1379 */         throw new UnsupportedOperationException(this.sort.getKey().toString());
/*      */       }
/* 1381 */       if (this.sort.getDirection() == SortBy.Direction.DESC) {
/* 1382 */         return o2.getId() - o1.getId();
/*      */       }
/* 1384 */       return o1.getId() - o2.getId();
/*      */     }
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/db/DbSearch.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */